﻿/********************************************************************************/
/* 	state.d																		*/
/*------------------------------------------------------------------------------*/
/*	製作		（　゜ワ゜）ノ　／　松久貫太									*/
/*	製作開始　	2007/05/11														*/
/*	MAIL		omega@personal.email.ne.jp										*/
/*	URL		http://nagoya.cool.ne.jp/o_mega										*/
/*																				*/
/*	このソースは「やわらかライセンス」の元で配布されています。					*/
/*-更新履歴---------------------------------------------------------------------*/
/*	2007/--/--																	*/
/*-その他-----------------------------------------------------------------------*/
/*	なし																		*/
/********************************************************************************/

/* ライブラリのインポート *******************************************************/
private import hell2;		// dHell2ライブラリ
private import vector;
private import keypad;
private import actor;
private import clickable;

private import std.string;
private import std.math;
private import std.math2;
private import std.stream;
private import std.thread;

/* グローバル定数定義 ***********************************************************/
GameState g_gamestate = null;


/* load *************************************************************************/
/*	リソースの読み込み															*/
/*-引数-------------------------------------------------------------------------*/
/*	なし																		*/
/*-返り値-----------------------------------------------------------------------*/
/*	なし																		*/
/********************************************************************************/
void load()
{
	Hell_loadTexture("font","resource/font/font.bmp" , [255,255,255]);
	Hell_loadTexture("circle","resource/image/circle.bmp" , [255,255,255]);
	
	Hell_write("resource loaded.\n");
}

/* GameState ********************************************************************/
/*	ゲーム全体の状態遷移を管理するクラス										*/
/********************************************************************************/
class GameState
{
private:
	int msg;				// リクエストメッセージ
public:
	enum
	{
		MSG_NONE,
		MSG_REQ_TITLE,			// タイトル状態へ　行きたい
		MSG_REQ_GAME,			// ゲーム
	}
	
	void move(){}
	void draw(){}
	
	// 設定
	void setMsg(int m)
	{
		msg = m;
	}
	// 取得
	int getMsg()
	{
		return msg;
	}
}



void loop()
{
	if(g_gamestate)
	{
		// メッセージ指示による状態遷移
		switch(g_gamestate.msg)
		{
			case GameState.MSG_NONE:
				break;
			case GameState.MSG_REQ_TITLE:
				g_gamestate = new TitleMain();
				break;
			case GameState.MSG_REQ_GAME:
				g_gamestate = new GameMain();
				break;
			default:
				Hell_write("Uknown msg at loop.");
		}
	}
	else
	{
		// 初期状態
		g_gamestate = new GameMain();
		Hell_write("Exec loop.\n");
	}
	
	g_gamestate.move();
	g_gamestate.draw();
}

/* TitleMain ********************************************************************/
/*	タイトルの状態																*/
/********************************************************************************/

class TitleMain : GameState
{
public:
	int timer;
	ClickablePool menu;
	
	this()
	{
		timer = 0;
				
		menu = new ClickablePool();
		menu.add("START" , new Vec3(280 , 400 , 0) , new Vec3(96 , 32 , 0));
	}
	
	void move()
	{
		timer++;
		
		string select = menu.getMouseClick();
		
		if(select == "START"){
			setMsg(GameState.MSG_REQ_GAME);
		}
	}
	
	void draw()
	{
		Hell_begin();
		resetPerspective();
		Hell_setAlpha(HELL_ALPHA_NORMAL);
		Hell_drawRect(0,0,640,480, 0, 0, 255,255,255,255);
		
		menu.draw();
				
		Hell_drawFont("SUMI" , 240, 160 , 4 , 255,255,255);
		Hell_drawFont("OMEGA WITH GAMEHELL2000" , 140, 480-48 , 2 , 160,160,160);
	}
}

/* GameMain *********************************************************************/
/*	ゲーム本体の状態															*/
/********************************************************************************/

class GameMain : GameState
{
public:
	int timer;
	Player player;
	Target target;
	int level;
	
	enum GAME_STATE{
		WAITING ,
		SETTING ,
		PLAYING , 
		CLEAR ,
		GAMEOVER ,
	}
	GAME_STATE state;
	
	this()
	{
		timer = 0;
		level = 0;
		state = GAME_STATE.WAITING;
		player = new Player();
		target = null;
	}
	
	void move()
	{
		timer++;
		
		// state
		
		if( state == GAME_STATE.WAITING ){
			if( timer > 10 && Hell_isPushMouse() ){
				state = GAME_STATE.SETTING;
				timer = 0;
			}
			player.move();
		}else if( state == GAME_STATE.SETTING ){
			if( target is null ){
				level ++;
				target = new Target( level + 1 );
			}else{
				target.move();
			}
			player.move();
			if( timer > 20 ) {
				state = GAME_STATE.PLAYING;
				timer = 0;
			}
		}else if( state == GAME_STATE.PLAYING ){
			player.move();
			player.life--;
			if( player.life < 0 ){
				state = GAME_STATE.GAMEOVER;
				timer = 0;
			}
			
			if( target ){
				target.move();
				
				if( target.collision( player.x , player.y ) ){
					target.addIndex();
				}
				
				if( target.checked_idx >= target.getTargetCount() ){
					state = GAME_STATE.SETTING;
					target = null;
					timer = 0;
					
					player.healLife( Player.MAX_LIFE / 2 );
				}
			}
		}else if( state == GAME_STATE.GAMEOVER ){
			player.move();
			if( target ){
				target.move();
			}
			
			if( Hell_isPushMouse() && timer > 10 ){
				setMsg( GameState.MSG_REQ_TITLE );
			}
		}
	}
	
	void draw()
	{
		Hell_begin();
		resetPerspective();
		Hell_setAlpha(HELL_ALPHA_ADD);
		Hell_drawRect(0,0,640,480,0,0,255,255,255,255);
		
		Hell_setAlpha(HELL_ALPHA_NORMAL);
		if( player ) player.draw();
		if( target ) target.draw();
		
		if( state == GAME_STATE.WAITING ){
			Hell_drawFont( "READY" , 280 + Hell_randInt(-2,2) , 200 + Hell_randInt(-2,2) , 2  );
		}else if(state == GAME_STATE.SETTING ){
			Hell_drawFont( " LV "~std.string.toString(level) , 280 + Hell_randInt(-2,2) , 200 + Hell_randInt(-2,2) , 2  );
		}else if( state == GAME_STATE.GAMEOVER ){
			Hell_drawFont( " ENDO" , 280 + Hell_randInt(-2,2) , 200 + Hell_randInt(-2,2) , 2  );
			Hell_drawFont( "REACHED LV "~std.string.toString(level) , 240  , 240  , 2  );
		}
		Hell_drawFPS();
	}
}

class Target{
	int timer;
	float[] x;
	float[] y;
	float size;
	int checked_idx;
	
	const float hit_size = 40;
	
	class TargetPosition{
		float x,y;
		
		this( float x , float y ){
			this.x = x;
			this.y = y;
		}
		
		this(){
			x = Hell_randInt( 32, 640-32 );
			y = Hell_randInt( 32, 480-32 );
		}
	};
	TargetPosition[] positions;
	
	this(int n){
		size = 4;
		timer = 0;
		checked_idx = 0;
		
		positions = new TargetPosition[n];
		foreach(inout p ; positions) p = new TargetPosition();
	}
	
	void move(){
		timer++;
	}
	
	bool collision( float x , float y ){
		if( checked_idx >= positions.length ) return false;
		
		float dx = x - positions[checked_idx].x;
		float dy = y - positions[checked_idx].y;
		if( dx * dx + dy * dy < hit_size * hit_size ) {
			return true;
		}
		return false;
	}
	
	void addIndex(){
		checked_idx++;
	}
	
	int getTargetCount(){
		return positions.length;
	}
	
	void draw(){
		foreach( int i , p ; positions ){
			if( i < checked_idx || timer - i*8 < 0) continue;
			
			float sz = 0.25;
			if( checked_idx == i ) sz = 4.0;
			if( checked_idx+1 == i ) sz = 2.0;
			if( checked_idx+2 == i ) sz = 1.0;
			if( checked_idx+3 == i ) sz = 0.;
			
			int alpha = min( i - checked_idx , 8 );
			Hell_drawTextureEx( "circle" , p.x , p.y , 0,0,64,64, 1.0/8*sz,1.0/8*sz , 0,255,255,255,255);
			Hell_drawTextureEx( "circle" , p.x , p.y , 0,0,64,64, 1.0/8*sz+0.2,1.0/8*sz+0.2 , 0,255,255,255,255/8);
			
			if( i == checked_idx ){
				float anime = (timer % 30) / 30.0;
				Hell_drawTextureEx( "circle" , p.x , p.y , 0,0,64,64, 1.0/8*sz+0.2+anime,1.0/8*sz+0.2+anime , 0,255,255,255,cast(int)(255/8*(1.0-anime)));
			}
			
			if( sz < 1 ) sz = 1;
			Hell_drawFont( std.string.toString(i+1) , cast(int)p.x , cast(int)p.y , sz ,255,255,255,255);
		}
	}
}

class Player{
	MouseRecorder mr;
	int timer ;
	float x,y;
	int life;
	
	static const int MAX_TAIL = 64;
	static const int MAX_LIFE = 30*5;
	
	class Tail{
		float x,y;
		float vx,vy;
		
		this(){
			x = 0;
			y = 0;
			vx = Hell_randInt( -100, 100 ) / 20.0;
			vy = Hell_randInt( -100, 100 ) / 20.0;
		};
		this(float x,float y){
			this();
			this.x = x;
			this.y = y;
		};
		
		void move(){
			x += vx;
			y += vy;
			vx *= 0.9;
			vy *= 0.9;
		}
	}
	
	Tail[] tail;
	
	this(){
		tail = new Tail[ MAX_TAIL ];
		mr = new MouseRecorder();
		foreach( inout t ; tail ) t = new Tail( mr.getX() , mr.getY() );
		timer = 0;
		life = MAX_LIFE;
	}
	
	void move(){
		for( int i = tail.length-1 ; i > 0 ; i -- ){
			tail[i] = tail[i-1];
		}
		if( life > 0 ){
			tail[0]= new Tail( mr.getX() , mr.getY() );
			x = mr.getX();
			y = mr.getY();
		}
		
		for( int i = tail.length-1 ; i > 0 ; i -- ){
			tail[i].move();
		}
		
		mr.update();
	}
	
	void healLife( int life ){
		this.life += life;
		if( this.life > MAX_LIFE ) this.life = MAX_LIFE;
	}
	
	void draw(){
		for( int i = 0 ; i < tail.length - 3 ; i+=1 ){
			float a = cast(float)(tail.length - i) / tail.length;
			float xx1 = (tail[i].x + tail[i+1].x)/2;
			float yy1 = (tail[i].y + tail[i+1].y)/2;
			float xx2 = (tail[i+1].x + tail[i+2].x)/2;
			float yy2 = (tail[i+1].y + tail[i+2].y)/2;
			drawLine( xx1,yy1 , tail[i+1].x , tail[i+1].y ,xx2,yy2, cast(int)(255 * a*a*a));
		}
		
		Hell_drawRect( 320-320*life/MAX_LIFE , 460 , 640*life/MAX_LIFE , 8 , 0,0, 0,0,0,255);
	}
	
	void drawLine(float x1,float y1, float x2, float y2 , float x3,float y3 , int alpha){
		const float r = 0.2;
		for(float i = 0 ; i < 1 ; i += r ){
			float xx1 = x1 * i*i + x2 * 2 * i*(1-i) + x3 * (1-i)*(1-i);
			float yy1 = y1 * i*i + y2 * 2 * i*(1-i) + y3 * (1-i)*(1-i);
			float t = i+r;
			float xx2 = x1 * t*t + x2 * 2 * t*(1-t) + x3 * (1-t)*(1-t);
			float yy2 = y1 * t*t + y2 * 2 * t*(1-t) + y3 * (1-t)*(1-t);
			
			Hell_drawLine( xx1 , yy1 , xx2 , yy2 ,1,0,0,0,alpha);
			Hell_drawLine( xx1 , yy1 , xx2 , yy2 ,8,0,0,0,alpha/8);
		}
	}
}

class Perticle{
}